package com.ruoyi.system.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.constant.*;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.dto.ModbusConfig;
import com.ruoyi.common.enums.ConstantsEnum;
import com.ruoyi.common.enums.DeviceStatus;
import com.ruoyi.common.enums.DeviceWarnStatus;
import com.ruoyi.common.enums.ProductEnum;
import com.ruoyi.common.utils.FrameBuilder;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.YiHuoUtils;
import com.ruoyi.common.utils.can.CanIoTool;
import com.ruoyi.common.utils.canopen.CanopenProtocolUtil;
import com.ruoyi.common.utils.canopen.CanopenResponse;
import com.ruoyi.common.utils.http.NetworkCheck;
import com.ruoyi.common.utils.modbus.Modbus4jWriteUtils;
import com.ruoyi.common.utils.modbus.TcpToSerialClient;
import com.ruoyi.common.utils.opcua.OpcUaServerUtil;
import com.ruoyi.common.utils.opcua.OpcUaUtil;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.domain.MrDevice;
import com.ruoyi.system.domain.MrDeviceAlarmData;
import com.ruoyi.system.domain.MrWaterTankDischargeInfo;
import com.ruoyi.system.domain.vo.ProcessResult;
import com.ruoyi.system.service.*;
import com.ruoyi.system.utils.ClassMethodNameUtil;
import com.ruoyi.system.utils.LogUtil;
import com.ruoyi.system.utils.MySQLSyncAndUpdate;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import org.apache.commons.collections4.CollectionUtils;
import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 定时任务 业务层处理
 *
 * @author lsw
 */
@Service
public class TaskServiceImpl implements TaskService {

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private ISysDictDataService sysDictDataService;

    @Autowired
    private IMrDeviceService mrDeviceService;

    @Autowired
    private IMrDeviceAlarmDataService mrDeviceAlarmDataService;

    @Autowired
    private SensorDataService sensorDataService;

    @Autowired
    private ISysConfigService configService;

    @Autowired
    private IMrWaterTankDischargeInfoService mrWaterTankDischargeInfoService;

    @Autowired
    private LogUtil logUtil;

    private static final Logger log = LoggerFactory.getLogger(TaskServiceImpl.class);

    /**
     * 数据库同步
     */
    @Override
    public void dataSynchronization() {
        mrDeviceAlarmDataService.dataSynchronization();
    }

    /**
     * 报警轮机员输出任务
     */
    @Override
    public void marineEngineerTask() {
        String outputSource = MaritimeConstants.MARINE_ENGINEER;
        MrDevice mr = mrDeviceService.getDeviceByOutputSource(outputSource);
        if (mr == null) {
            log.error("【marineEngineer】轮机员无数据");
            return;
        }
        String systemId2 = mr.getSystemId();
        // 记录报警时间
        if (redisCache.hasKey(systemId2)) {
            Long startTime = redisCache.getCacheObject(systemId2);
            Long endTime = System.currentTimeMillis();
            // 延时时间
            int delayTime = NumberConstants.ONE_HUNDRED_EIGHTY;
            if (StringUtils.isNotEmpty(mr.getDelayTime())) {
                delayTime = Integer.parseInt(mr.getDelayTime());
            }
            if ((endTime - startTime) / NumberConstants.ONE_THOUSAND > delayTime) {
                // 报警轮机员输出
                mrDeviceService.mute(true, outputSource);
            }
        }
    }

    /**
     * 信息化上报任务
     *
     * @param port    端口
     * @param slaveId 地址码
     */
    @Override
    public void informationReportingTask(int port, int slaveId) {
        // 1. 获取并过滤设备列表
        List<MrDevice> validDevices = getAndFilterDevices(port);

        // 2. 处理设备数据
        ProcessResult result = processDevices(validDevices);

        // 3. 执行Modbus写入操作
        executeModbusWrites(port, slaveId, result.getDeviceList(), result.getRegisterMap());

        // 4. 更新主设备状态
        updateMasterDeviceStatus(port);
    }

    // 辅助方法 - 获取并过滤设备列表
    private List<MrDevice> getAndFilterDevices(int port) {
        MrDevice query = new MrDevice();
        query.setStatus(DeviceStatus.ACTIVE.getValue());
        query.setPort(String.valueOf(port));
        List<MrDevice> devices = mrDeviceService.selectMrDeviceList(query);

        return devices.stream()
                .filter(device -> StringUtils.isNotEmpty(device.getRegisterAddress()))
                .filter(device -> StringUtils.isNotEmpty(device.getOutputSource()))
                .map(device -> {
                    MrDevice sourceDevice = mrDeviceService.getDeviceBySystemId(device.getOutputSource());
                    return new AbstractMap.SimpleEntry<>(device, sourceDevice);
                })
                .filter(entry -> entry.getValue() != null)
                .filter(entry -> !DeviceWarnStatus.NORMAL.getValue().equals(entry.getValue().getWarnStatus()))
                .filter(entry -> StringUtils.isNotEmpty(entry.getValue().getRemark()))
                .map(AbstractMap.SimpleEntry::getKey)
                .collect(Collectors.toList());
    }

    // 辅助方法 - 处理设备数据
    private ProcessResult processDevices(List<MrDevice> devices) {
        List<MrDevice> deviceList = new ArrayList<>();
        Map<String, StringBuilder> registerMap = new HashMap<>();

        for (MrDevice device : devices) {
            MrDevice sourceDevice = mrDeviceService.getDeviceBySystemId(device.getOutputSource());
            String remark = sourceDevice.getRemark();
            String oldRemark = device.getRemark();

            if (ProductEnum.MR.getValue().equals(device.getModality())) {
                processMrDevice(device, remark, registerMap);
            } else {
                processOtherDevice(device, remark, deviceList);
            }

            updateDeviceStatus(device, sourceDevice);

            if (Boolean.TRUE.toString().equals(remark) && Boolean.FALSE.toString().equals(oldRemark)) {
                triggerDeviceAlarm(device);
            }
        }

        return new ProcessResult(deviceList, registerMap);
    }

    // 辅助方法 - 处理MR类型设备
    private void processMrDevice(MrDevice device, String remark, Map<String, StringBuilder> registerMap) {
        if (!ProductEnum.TYPE_DEVICE_OUTPUT.getValue().equals(device.getProduct()) ||
                StringUtils.isEmpty(device.getSwitchingPosition())) {
            return;
        }

        String registerAddress = device.getRegisterAddress();
        StringBuilder sb = registerMap.computeIfAbsent(registerAddress, k -> {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < 16; i++) builder.append(DigitStringConstants.ZERO);
            return builder;
        });

        if (Boolean.TRUE.toString().equals(remark)) {
            int position = NumberConstants.FIFTEEN - Integer.parseInt(device.getSwitchingPosition());
            sb.setCharAt(position, '1');
        }

        device.setRemark(remark);
    }

    // 辅助方法 - 处理其他类型设备
    private void processOtherDevice(MrDevice device, String remark, List<MrDevice> deviceList) {
        if (StringUtils.isEmpty(device.getOutMultiplyCoefficient())) return;

        String[] factors = device.getOutMultiplyCoefficient().split(StringConstants.HYPHEN);
        double value = Double.parseDouble(remark);

        if (factors.length == NumberConstants.TWO) {
            value = value * Double.parseDouble(factors[0]) - Double.parseDouble(factors[1]);
        } else {
            value *= Double.parseDouble(device.getOutMultiplyCoefficient());
        }

        device.setRemark(String.valueOf(value));
        deviceList.add(device);
    }

    // 辅助方法 - 更新设备状态
    private void updateDeviceStatus(MrDevice device, MrDevice sourceDevice) {
        MrDevice update = new MrDevice();
        update.setDeviceId(device.getDeviceId());
        update.setWarnStatus(sourceDevice.getWarnStatus());
        update.setLastHeartbeatTime(new Date());
        update.setRemark(device.getRemark());
        mrDeviceService.updateMrDevice(update);
    }

    // 辅助方法 - 触发设备报警
    private void triggerDeviceAlarm(MrDevice device) {
        String content = saveDeviceAlarm(device);
        sensorDataService.saveRedis(device.getDeviceId().toString(), content);
        redisCache.setCacheObject(ConstantsEnum.LAST_WARN_TIME.getValue() + device.getSystemId(), new Date().getTime());
    }

    // 辅助方法 - 执行Modbus写入操作
    private void executeModbusWrites(int port, int slaveId, List<MrDevice> devices,
                                     Map<String, StringBuilder> registerMap) {
        String host = ConstantsEnum.LOCALHOST.getValue();

        // 处理单个设备写入
        devices.forEach(device -> {
            if (StringUtils.isEmpty(device.getHost()) || StringUtils.isEmpty(device.getRegisterAddress())) {
                return;
            }

            int offset = Integer.parseInt(device.getRegisterAddress());
            String value = device.getRemark();

            try {
                if (ProductEnum.BA.getValue().equals(device.getModality())) {
                    writeAnalogValue(host, port, slaveId, offset, value);
                } else if (ProductEnum.MR.getValue().equals(device.getModality())) {
                    writeDigitalValue(host, port, slaveId, offset, value, device);
                }
            } catch (ModbusTransportException | ModbusInitException e) {
                log.error("【信息化上报任务】{}量输出失败",
                        ProductEnum.BA.getValue().equals(device.getModality()) ? "模拟" : "开关", e);
            }
        });

        // 处理寄存器映射写入
        registerMap.forEach((address, binaryStr) -> {
            int decimal = Integer.parseInt(binaryStr.toString(), 2);
            try {
                Modbus4jWriteUtils.writeRegister(host, port, slaveId,
                        Integer.parseInt(address), (short) decimal);
            } catch (ModbusTransportException | ModbusInitException e) {
                log.error("【信息化上报任务】开关量输出失败", e);
            }
        });
    }

    // 辅助方法 - 写入模拟量值
    private void writeAnalogValue(String host, int port, int slaveId, int offset, String value)
            throws ModbusTransportException, ModbusInitException {
        if (StringUtils.isEmpty(value)) return;

        short writeValue = (short) Double.parseDouble(value);
        Modbus4jWriteUtils.writeRegister(host, port, slaveId, offset, writeValue);
    }

    // 辅助方法 - 写入数字量值
    private void writeDigitalValue(String host, int port, int slaveId, int offset,
                                   String value, MrDevice device)
            throws ModbusTransportException, ModbusInitException {
        if (ProductEnum.TYPE_DEVICE_OUTPUT.getValue().equals(device.getProduct())) {
            short writeValue = (short) Double.parseDouble(value);
            Modbus4jWriteUtils.writeRegister(host, port, slaveId, offset, writeValue);
        } else {
            boolean writeValue = Boolean.parseBoolean(value);
            Modbus4jWriteUtils.writeCoil(host, port, slaveId, offset, writeValue);
        }
    }

    // 辅助方法 - 更新主设备状态
    private void updateMasterDeviceStatus(int port) {
        MrDevice masterDevice = mrDeviceService.getDeviceByOutputSource(String.valueOf(port));
        if (masterDevice == null) return;

        redisCache.deleteObject(ConstantsEnum.DELAY_TIME.getValue() + masterDevice.getSystemId());

        MrDevice update = new MrDevice();
        update.setDeviceId(masterDevice.getDeviceId());
        update.setWarnStatus(DeviceWarnStatus.LINE.getValue());
        update.setLastHeartbeatTime(new Date());
        update.setRemark(Boolean.FALSE.toString());
        mrDeviceService.updateMrDevice(update);

        sensorDataService.saveRedis(masterDevice.getDeviceId().toString(), null);
        redisCache.deleteObject(ConstantsEnum.LAST_WARN_TIME.getValue() + masterDevice.getSystemId());
    }

    private String saveDeviceAlarm(MrDevice mr) {
        MrDeviceAlarmData mrDeviceAlarmData = new MrDeviceAlarmData();
        String content = mr.getSystemId() + StringConstants.COLON + mr.getDeviceName() + StringConstants.UNDERSCORE + StringConstants.DEVICE_ALARM + StringConstants.TRIPLE_PERCENT + mr.getDisplayAlarm();
        mrDeviceAlarmData.setUnit(StringUtils.isNotEmpty(mr.getUnit()) ? mr.getUnit() : StringConstants.KILOGRAM);
        mrDeviceAlarmData.setAlarmTime(new Date());
        mrDeviceAlarmData.setDeviceCode(mr.getSystemId());
        mrDeviceAlarmData.setDeviceName(mr.getDeviceName());
        mrDeviceAlarmData.setContent(content.split(StringConstants.TRIPLE_PERCENT)[0]);
        mrDeviceAlarmData.setDisplayAlarm(mr.getDisplayAlarm());
        mrDeviceAlarmDataService.insertMrDeviceAlarmData(mrDeviceAlarmData);
        return content;
    }

    /**
     * VDR上报任务
     *
     * @param host IP
     * @param port 端口
     */
    @Override
    public void vdrReportingTask(String host, String port) {
        // 获取设备信息
        MrDevice mrDevice = new MrDevice();
        mrDevice.setStatus(DeviceStatus.ACTIVE.getValue());
        mrDevice.setPort(String.valueOf(port));
        List<MrDevice> mrDevices = mrDeviceService.selectMrDeviceList(mrDevice);
        StringBuilder dataToSend = new StringBuilder("HXDQ,1,1,");
        for (MrDevice mr : mrDevices) {
            // TODO 输出源会导致有输出信号产生杂波，建议换成关联报警
            if (StringUtils.isEmpty(mr.getOutputSource())) {
                continue;
            }
            MrDevice mrDevice1 = mrDeviceService.getDeviceBySystemId(mr.getOutputSource());
            if (mrDevice1 == null) {
                continue;
            }
            String remark = mrDevice1.getRemark();
            // 开关量
            if (mr.getModality().equals(ProductEnum.MR.getValue())) {
                if (StringUtils.isNotEmpty(remark)) {
                    dataToSend.append(Boolean.TRUE.toString().equals(remark) ? DigitStringConstants.ONE : DigitStringConstants.ZERO).append(StringConstants.COMMA);
                } else {
                    dataToSend.append(DigitStringConstants.ZERO);
                    dataToSend.append(StringConstants.COMMA);
                }
            }
            if (Boolean.TRUE.toString().equals(remark) && Boolean.FALSE.toString().equals(mr.getRemark())) {
                String content = saveDeviceAlarm(mr);
                // redis 处理
                sensorDataService.saveRedis(mr.getDeviceId().toString(), content);
                redisCache.setCacheObject(ConstantsEnum.LAST_WARN_TIME.getValue() + mr.getSystemId(), new Date().getTime());
            }
            // 更新数据
            mr.setRemark(remark);
            updateDeviceStatus(mr, mrDevice1);
        }
        String content = YiHuoUtils.yiHuo(dataToSend.toString());
        dataToSend.append(StringConstants.ASTERISK).append(content);
        // 要添加的字符
        char charToAdd = StringConstants.DOLLAR;
        dataToSend.insert(0, charToAdd);
        String msg = "【vdrReportingTask】VDR通讯入参：" + dataToSend + " , host: " + host + " , port: " + port;
        logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
        // 构建帧
        byte[] frame = FrameBuilder.buildFrame(dataToSend.toString(), true);
        String frameStr = FrameBuilder.bytesToHex(frame);
        msg = "【vdrReportingTask】VDR通讯入参：" + frameStr + " , host: " + host + " , port: " + port;
        logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
        String responseData = TcpToSerialClient.sendFrame(host, Integer.parseInt(port), frame);
        msg = "【vdrReportingTask】VDR通讯返回：" + responseData;
        logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
        // 更新通道状态
        if (responseData == null) {
            sensorDataService.updateDeviceStatus(port);
        } else {
            sensorDataService.channelAlarm(Integer.parseInt(port));
        }
    }

    /**
     * UDP 协议上报任务
     *
     * @param host     IP
     * @param port     端口
     * @param dataSize 数据大小
     */
    @Override
    public void udpReportingStringTask(String host, String port, String dataSize) {
        // 获取设备信息
        MrDevice mrDevice = new MrDevice();
        mrDevice.setStatus(DeviceStatus.ACTIVE.getValue());
        List<MrDevice> mrDevices = mrDeviceService.selectMrDeviceList(mrDevice);
        String cate = StringConstants.SPACE;
        StringBuilder dataList = null;
        try (DatagramSocket socket = new DatagramSocket()) {
            InetAddress address = InetAddress.getByName(host);
            for (MrDevice mr : mrDevices) {
                String value = DigitStringConstants.ZERO;
                if (mr.getModality().equals(ProductEnum.MR.getValue()) && DeviceWarnStatus.ALARM.getValue().equals(mr.getWarnStatus())) {
                    value = DigitStringConstants.ONE;
                }
                if (ProductEnum.BA.getValue().equals(mr.getModality())) {
                    value = mr.getRemark();
                }
                if (dataList == null) {
                    dataList = new StringBuilder(mr.getSystemId() + ":" + value + ",");
                } else {
                    dataList.append(mr.getSystemId()).append(":").append(value).append(",");
                }
                int num = dataList.toString().split(",").length;
                if (num >= Integer.parseInt(dataSize) || (!cate.equals(StringConstants.SPACE) && !cate.equals(String.valueOf(mr.getBelongId())))) {
                    sendDataBatch(socket, address, Integer.parseInt(port), dataList.toString(), cate);
                    dataList = null;
                }
                cate = String.valueOf(mr.getBelongId());
            }
            // 处理剩余不足 30 条的数据
            if (dataList != null) {
                sendDataBatch(socket, address, Integer.parseInt(port), dataList.toString(), cate);
            }
        } catch (IOException e) {
            log.error("UDP 发送过程中出现错误: " + e.getMessage());
        }
    }

    /**
     * UDP 协议上报任务
     *
     * @param host     IP
     * @param port     端口
     * @param dataSize 数据大小
     */
    @Override
    public void udpReportingTask(String host, String port, String dataSize) {
        // 获取设备信息
        MrDevice mrDevice = new MrDevice();
        mrDevice.setStatus(DeviceStatus.ACTIVE.getValue());
        List<MrDevice> mrDevices = mrDeviceService.selectMrDeviceList(mrDevice);
        String cate = StringConstants.SPACE;
        List<Map<String, String>> dataList = new ArrayList<>();
        try (DatagramSocket socket = new DatagramSocket()) {
            InetAddress address = InetAddress.getByName(host);
            for (MrDevice mr : mrDevices) {
                Map<String, String> dataItem = getStringStringMap(mr);
                dataList.add(dataItem);

                if (dataList.size() >= Integer.parseInt(dataSize) || (!cate.equals(StringConstants.SPACE) && !cate.equals(String.valueOf(mr.getBelongId())))) {
                    sendDataBatch(socket, address, Integer.parseInt(port), dataList, cate);
                    dataList.clear();
                }
                cate = String.valueOf(mr.getBelongId());
            }
            // 处理剩余不足 30 条的数据
            if (!dataList.isEmpty()) {
                sendDataBatch(socket, address, Integer.parseInt(port), dataList, cate);
            }
        } catch (IOException e) {
            log.error("UDP 发送过程中出现错误: " + e.getMessage());
        }
    }

    private static Map<String, String> getStringStringMap(MrDevice mr) {
        String value = DigitStringConstants.ZERO;
        if (mr.getModality().equals(ProductEnum.MR.getValue()) && DeviceWarnStatus.ALARM.getValue().equals(mr.getWarnStatus())) {
            value = DigitStringConstants.ONE;
        }
        if (ProductEnum.BA.getValue().equals(mr.getModality())) {
            value = mr.getRemark();
        }
        Map<String, String> dataItem = new HashMap<>();
        dataItem.put("ID", mr.getSystemId());
        dataItem.put("value", value);
        return dataItem;
    }

    @Override
    public void networkCheck() {
        // 1. 参数校验
        String systemId2 = sysDictDataService.selectDictValue(ConstantsEnum.ALARM_SHIELDING.getValue(), ConstantsEnum.MONITORING_ALARM_SYSTEM.getValue());
        MrDevice targetDevice = validateAndGetDevice(systemId2);
        if (targetDevice == null) return;

        // 2. 获取配置信息
        List<SysDictData> networkConfigs = getNetworkConfigurations();
        if (CollectionUtils.isEmpty(networkConfigs)) return;

        // 3. 执行网络检查
        Map<String, Boolean> connectionResults = performNetworkChecks(networkConfigs);

        // 3.5 网关处理
//        gatewayProcessing(targetDevice);

        // 4. 处理检查结果
        processCheckResults(networkConfigs, connectionResults, targetDevice);

        // 5. 更新目标设备状态
        updateTargetDeviceStatus(targetDevice);
    }

    // 辅助方法 - 网关处理
    private void gatewayProcessing(MrDevice targetDevice) {
        List<SysDictData> sysDictDataList = sysDictDataService.selectDictDataByType(HeartbeatDetectionConstants.GATEWAY_HEARTBEAT_CONFIGURATION);
        for (SysDictData sysDictData : sysDictDataList) {
            MrDevice device = mrDeviceService.getDeviceBySystemId(sysDictData.getDictValue());
            if (device == null) continue;

            redisCache.setCacheObject(ConstantsEnum.MONITORING_ALARM_SYSTEM.getValue(), false);
            boolean flag = handleDeviceAlarm(device, targetDevice);
            if (device.getWarnStatus().equals(DeviceWarnStatus.ALARM.getValue())) {
                // 更新设备状态
                MrDevice update = new MrDevice();
                update.setDeviceId(device.getDeviceId());
                update.setLastHeartbeatTime(new Date());
                mrDeviceService.updateMrDevice(update);
            }
            handleAssociatedParameters(device, flag);
        }
    }

    // 辅助方法 - 参数校验并获取设备
    private MrDevice validateAndGetDevice(String systemId) {
        MrDevice device = mrDeviceService.getDeviceBySystemId(systemId);
        if (device == null) {
            log.error("【networkCheck】参数错误，systemId：{}", systemId);
        }
        return device;
    }

    // 辅助方法 - 获取网络配置
    private List<SysDictData> getNetworkConfigurations() {
        String networkCheckKey = ConstantsEnum.NETWORK_CHECK.getValue();
        return sysDictDataService.selectDictDataByType(networkCheckKey);
    }

    // 辅助方法 - 执行网络检查
    private Map<String, Boolean> performNetworkChecks(List<SysDictData> configs) {
        List<String> ips = configs.stream().map(SysDictData::getDictLabel).collect(Collectors.toList());
        return NetworkCheck.bathCheckConnect(ips);
    }

    // 辅助方法 - 处理检查结果
    private void processCheckResults(List<SysDictData> configs, Map<String, Boolean> results, MrDevice targetDevice) {
        if (!redisCache.hasKey(ConstantsEnum.MONITORING_ALARM_SYSTEM.getValue())) {
            redisCache.setCacheObject(ConstantsEnum.MONITORING_ALARM_SYSTEM.getValue(), true);
        }
        for (SysDictData config : configs) {
            MrDevice device = mrDeviceService.getDeviceBySystemId(config.getDictValue());
            if (device == null) continue;

            boolean isConnected = results.getOrDefault(config.getDictLabel(), false);
            updateDeviceStatus(device, isConnected);

            if (!isConnected) {
                redisCache.setCacheObject(ConstantsEnum.MONITORING_ALARM_SYSTEM.getValue(), false);
                handleDeviceAlarm(device, targetDevice);
            } else {
                clearDeviceAlarm(device);
            }

            handleAssociatedParameters(device, isConnected);
        }

    }

    // 辅助方法 - 更新设备状态
    private void updateDeviceStatus(MrDevice device, boolean isConnected) {
        MrDevice update = new MrDevice();
        update.setDeviceId(device.getDeviceId());
        update.setRemark(String.valueOf(!isConnected));
        update.setWarnStatus(isConnected ? DeviceWarnStatus.LINE.getValue() : device.getWarnStatus());
        update.setLastHeartbeatTime(new Date());
        mrDeviceService.updateMrDevice(update);
    }

    // 辅助方法 - 处理设备报警
    private boolean handleDeviceAlarm(MrDevice device, MrDevice targetDevice) {
        boolean flag = false;
        if (!device.getWarnStatus().equals(DeviceWarnStatus.ALARM.getValue())) {
            flag = checkAndTriggerAlarm(device);
        }

        if (!targetDevice.getWarnStatus().equals(DeviceWarnStatus.ALARM.getValue())) {
            flag = checkAndTriggerAlarm(targetDevice);
        }
        return flag;
    }

    // 辅助方法 - 检查并触发报警
    private boolean checkAndTriggerAlarm(MrDevice device) {
        boolean flag = false;
        int delayTime = StringUtils.isEmpty(device.getDelayTime()) ? NumberConstants.FIVE : Integer.parseInt(device.getDelayTime());
        long lastWarnTime = sensorDataService.getLastWarnTime(device.getSystemId());
        long timeDifference = (new Date().getTime() - lastWarnTime) / NumberConstants.ONE_THOUSAND;

        if (timeDifference > delayTime) {
            triggerAlarm(device);
            flag = true;
        }
        return flag;
    }

    // 辅助方法 - 触发报警
    private void triggerAlarm(MrDevice device) {
        // 更新设备状态
        MrDevice update = new MrDevice();
        update.setDeviceId(device.getDeviceId());
        update.setWarnStatus(DeviceWarnStatus.ALARM.getValue());
        update.setLastHeartbeatTime(new Date());
        mrDeviceService.updateMrDevice(update);

        // 保存报警信息 更新Redis状态
        String content = saveDeviceAlarm(device);
        sensorDataService.saveRedis(device.getDeviceId().toString(), content);
        redisCache.setCacheObject(ConstantsEnum.LAST_WARN_TIME.getValue() + device.getSystemId(), new Date().getTime());
        redisCache.setCacheObject(ConstantsEnum.MARINE_ENGINEER_STATUS.getValue(), true);

        // 触发蜂鸣器
        mrDeviceService.mute(true, MaritimeConstants.BUZZER);

        // 通知轮机员
        sensorDataService.marineEngineer(device.getSystemId(), "networkCheck");

    }

    // 辅助方法 - 清除报警
    private void clearDeviceAlarm(MrDevice device) {
        sensorDataService.saveRedis(device.getDeviceId().toString(), null);
        redisCache.deleteObject(ConstantsEnum.LAST_WARN_TIME.getValue() + device.getSystemId());
        String devWarnCacheKey = ConstantsEnum.DEV_WARN_CACHE.getValue();
        redisCache.setCacheObject(devWarnCacheKey + device.getSystemId(), NumberConstants.ZERO);
    }

    // 辅助方法 - 处理关联参数
    private void handleAssociatedParameters(MrDevice device, boolean isConnected) {
        if (StringUtils.isNotEmpty(device.getAssociateInternalParameters())) {
            sensorDataService.associatedAlarm(device.getAssociateInternalParameters(), isConnected);
        }
    }

    // 辅助方法 - 更新目标设备状态
    private void updateTargetDeviceStatus(MrDevice device) {
        MrDevice update = new MrDevice();
        update.setDeviceId(device.getDeviceId());
        update.setLastHeartbeatTime(new Date());
        boolean allHealthy = redisCache.getCacheObject(ConstantsEnum.MONITORING_ALARM_SYSTEM.getValue());
        if (allHealthy) {
            update.setWarnStatus(DeviceWarnStatus.LINE.getValue());
            update.setRemark(Boolean.FALSE.toString());
            sensorDataService.saveRedis(device.getDeviceId().toString(), null);
            redisCache.deleteObject(ConstantsEnum.LAST_WARN_TIME.getValue() + device.getSystemId());
        }
        redisCache.deleteObject(ConstantsEnum.MONITORING_ALARM_SYSTEM.getValue());
        mrDeviceService.updateMrDevice(update);
    }

    /**
     * 数据库一键同步
     */
    @Override
    public void mySQLSyncAndUpdate() {
        String sourceHost = ConstantsEnum.SERVER_124.getValue();
        String targetHost = ConstantsEnum.SERVER_125.getValue();
        if (!configService.selectDataEnabled()) {
            sourceHost = ConstantsEnum.SERVER_125.getValue();
            targetHost = ConstantsEnum.SERVER_124.getValue();
        }
        // 创建工具类实例
        MySQLSyncAndUpdate syncTool = new MySQLSyncAndUpdate();
        String msg = syncTool.run(sourceHost, targetHost);
        logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
    }

    @Override
    public void upcuaReportingStringTask(String endpointUrl, String dataSize) {
        // 获取设备信息
        MrDevice mrDevice = new MrDevice();
        mrDevice.setStatus(DeviceStatus.ACTIVE.getValue());
        List<MrDevice> mrDevices = mrDeviceService.selectMrDeviceList(mrDevice);
        Map<String, Object> nodeValueMap = new HashMap<>();
        for (MrDevice device : mrDevices) {
            String key = "ns=2;s=" + device.getSystemId();
            String nodeValue = device.getRemark() == null || device.getRemark().isEmpty() ? "0" : device.getRemark();
            nodeValueMap.put(key, nodeValue);
        }
        Map<String, StatusCode> results = OpcUaUtil.writeValues(endpointUrl, nodeValueMap, null, null, Integer.parseInt(dataSize));
        // 检查写入结果
        for (Map.Entry<String, StatusCode> entry : results.entrySet()) {
            String msg = "节点: " + entry.getKey() + ", 写入成功: " + entry.getValue().isGood();
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
        }
    }

    @Override
    public void upcuaServerTask(String upcuaServerIp, String upcuaServerPort) {
        // 获取设备信息（仅处理活跃状态的设备）
        MrDevice mrDevice = new MrDevice();
        mrDevice.setStatus(DeviceStatus.ACTIVE.getValue());
        List<MrDevice> mrDevices = mrDeviceService.selectMrDeviceList(mrDevice);
        logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), "开始处理设备同步，共" + mrDevices.size() + "个活跃设备");

        // 分类设备：outputSource非空→newValues（需从OPC读取后回写）；空→values（直接同步到OPC）
        Map<String, MrDevice> newValues = new HashMap<>(); // 存储设备对象，便于后续回写
        Map<String, Object> values = new HashMap<>();
        List<OpcUaServerUtil.NodeInfo> nodeInfos = new ArrayList<>();
        for (MrDevice device : mrDevices) {
            String systemId = device.getSystemId();
            if (systemId == null || systemId.isEmpty()) {
                logUtil.warn(ClassMethodNameUtil.getSimpleMethodName(), "设备ID为" + device.getDeviceId() + "的systemId为空，跳过处理");
                continue;
            }

            if (StringUtils.isNotEmpty(device.getOutputSource())) {
                newValues.put(systemId, device);
                logUtil.debug(ClassMethodNameUtil.getSimpleMethodName(), "添加到newValues，systemId=" + systemId + ", outputSource=" + device.getOutputSource());
            } else {
                // outputSource为空，直接用remark同步到OPC
                String nodeValue = device.getRemark() == null || device.getRemark().isEmpty() ? "0" : device.getRemark();
                values.put(systemId, nodeValue);
                nodeInfos.add(new OpcUaServerUtil.NodeInfo(systemId, nodeValue, device.getDeviceName()));
                // 更新设备状态（警告状态和心跳时间）
                MrDevice updatedDevice = new MrDevice();
                updatedDevice.setDeviceId(device.getDeviceId());
                updatedDevice.setWarnStatus(DeviceWarnStatus.LINE.getValue());
                updatedDevice.setLastHeartbeatTime(new Date());
                if (!sensorDataService.shouldSkipByDelayTime(device, 30)) {
                    mrDeviceService.updateMrDevice(updatedDevice);
                }
                logUtil.debug(ClassMethodNameUtil.getSimpleMethodName(), "添加到values，systemId=" + systemId + ", 节点值=" + nodeValue);
            }
        }

        // 1. 同步values到OPC（直接创建/更新节点）
        if (!values.isEmpty()) {
            // 调用带备注的节点创建方法
            OpcUaServerUtil.updateNodesWithDescription(upcuaServerIp, Integer.parseInt(upcuaServerPort), nodeInfos);
//            OpcUaServerUtil.updateNodes(upcuaServerIp, Integer.parseInt(upcuaServerPort), values);
        } else {
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), "values为空，无需更新节点");
        }

        // 2. 处理newValues：确保节点存在，再读取值回写设备
        if (!newValues.isEmpty()) {
            List<String> nodeIds = new ArrayList<>(newValues.keySet());
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), "开始处理newValues节点，共" + nodeIds.size() + "个，节点ID：" + nodeIds);

            // 2.1 读取OPC中当前节点值
            Map<String, Object> currentValues = OpcUaServerUtil.readNodes(upcuaServerIp, Integer.parseInt(upcuaServerPort), nodeIds);
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), "读取newValues节点结果，返回" + currentValues.size() + "个值");

            // 2.2 校验节点是否真的存在（过滤值为null或读取失败的节点）
            List<String> missingNodeIds = new ArrayList<>();
            for (String nodeId : nodeIds) {
                Object value = currentValues.get(nodeId);
                if (value == null) {
                    missingNodeIds.add(nodeId);
                    logUtil.warn(ClassMethodNameUtil.getSimpleMethodName(), "节点" + nodeId + "不存在或值为null，需要创建");
                }
            }

            // 2.3 补充创建缺失的节点
            if (!missingNodeIds.isEmpty()) {
                Map<String, Object> nodeValuesToCreate = new HashMap<>();
                List<OpcUaServerUtil.NodeInfo> nodeInfoToCreate = new ArrayList<>();
                for (String nodeId : missingNodeIds) {
                    MrDevice device = newValues.get(nodeId);
                    String initValue = device.getRemark() == null || device.getRemark().isEmpty() ? "0" : device.getRemark();
                    nodeValuesToCreate.put(nodeId, initValue);
                    nodeInfoToCreate.add(new OpcUaServerUtil.NodeInfo(nodeId, initValue, device.getDeviceName()));
                }
                // 调用带备注的节点创建方法
                OpcUaServerUtil.updateNodesWithDescription(upcuaServerIp, Integer.parseInt(upcuaServerPort), nodeInfoToCreate);
//                OpcUaServerUtil.updateNodes(upcuaServerIp, Integer.parseInt(upcuaServerPort), nodeValuesToCreate);

                // 重新读取创建后的节点值
                currentValues = OpcUaServerUtil.readNodes(upcuaServerIp, Integer.parseInt(upcuaServerPort), nodeIds);
                logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), "重新读取newValues节点，返回" + currentValues.size() + "个值");
            }

            // 2.4 将OPC节点值回写到设备
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), "开始将OPC节点值回写设备");
            for (Map.Entry<String, Object> entry : currentValues.entrySet()) {
                String systemId = entry.getKey();
                Object nodeValue = entry.getValue();
                MrDevice device = newValues.get(systemId);

                if (device == null) {
                    logUtil.warn(ClassMethodNameUtil.getSimpleMethodName(), "未找到systemId=" + systemId + "对应的设备，跳过回写");
                    continue;
                }

                // 日志记录节点值（区分null和正常值）
                String valueStr = (nodeValue == null) ? "null（可能未创建成功）" : nodeValue.toString();
                logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), "systemId:" + systemId + ", 节点key:" + systemId + ", 节点值:" + valueStr);

                // 若节点值有效且与设备remark不一致，则更新设备
                if (nodeValue != null) {
                    String nodeValueStr = nodeValue.toString();
                    MrDevice updatedDevice = new MrDevice();
                    updatedDevice.setDeviceId(device.getDeviceId());
                    updatedDevice.setLastHeartbeatTime(new Date());
                    if (!nodeValueStr.equals(device.getRemark())) {
                        updatedDevice.setWarnStatus(DeviceWarnStatus.LINE.getValue());
                        if (ProductEnum.MR.getValue().equals(device.getModality()) && DigitStringConstants.ONE.equals(nodeValueStr)) {
                            updatedDevice.setWarnStatus(DeviceWarnStatus.ALARM.getValue());
                        }
                        updatedDevice.setRemark(nodeValueStr);
                        mrDeviceService.updateMrDevice(updatedDevice);
                    } else {
                        if (!sensorDataService.shouldSkipByDelayTime(device, 30)) {
                            mrDeviceService.updateMrDevice(updatedDevice);
                        }
                    }
                    logUtil.debug(ClassMethodNameUtil.getSimpleMethodName(), "更新设备" + device.getDeviceId() + "的remark为：" + nodeValueStr);
                } else {
                    logUtil.error(ClassMethodNameUtil.getSimpleMethodName(), "节点" + systemId + "值为null，无法回写设备");
                }
            }
        } else {
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), "newValues为空，无需处理节点回写");
        }
    }


    @Override
    public void canTask(String ip, int port, int moduleAddr, int type) {
        // 获取设备信息
        MrDevice mrDevice = new MrDevice();
        mrDevice.setStatus(DeviceStatus.ACTIVE.getValue());
        mrDevice.setHost(ip);
        mrDevice.setPort(String.valueOf(port));
        mrDevice.setRegisterAddress(String.valueOf(moduleAddr));
        List<MrDevice> mrDevices = mrDeviceService.selectMrDeviceList(mrDevice);

        CanIoTool tool = new CanIoTool(ip, port, moduleAddr);
        if (type == 1) {
            Map<Integer, Boolean> readDigital = null;
            try {
                readDigital = tool.readDigitalInput();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            String msg = "【canTask】开关量采样状态：" + readDigital;
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
            if (!readDigital.isEmpty()) {
                for (MrDevice mrDevice1 : mrDevices) {
                    boolean value = readDigital.get(Integer.parseInt(mrDevice1.getSwitchingPosition()));
                    MrDevice update = new MrDevice();
                    update.setDeviceId(mrDevice1.getDeviceId());
                    update.setLastHeartbeatTime(new Date());
                    update.setWarnStatus(value ? DeviceWarnStatus.ALARM.getValue() : DeviceWarnStatus.LINE.getValue());
                    update.setRemark(String.valueOf(value));
                    mrDeviceService.updateMrDevice(update);
                }
            }
        }
        if (type == 2) {
            Map<Integer, Boolean> digitalStates = new HashMap<>();
            for (MrDevice mrDevice1 : mrDevices) {
                digitalStates.put(Integer.valueOf(mrDevice1.getSwitchingPosition()), mrDevice1.getRemark().equals("true"));
            }
            try {
                tool.writeDigitalOutput(digitalStates);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            String msg = "【canTask】开关量输出状态：" + digitalStates;
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
        }
        if (type == 3) {
            Map<Integer, Boolean> readDigital = null;
            try {
                readDigital = tool.readDigitalOutput();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            String msg = "【canTask】开关量输出读取状态：" + readDigital;
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
            if (!readDigital.isEmpty()) {
                for (MrDevice mrDevice1 : mrDevices) {
                    boolean value = readDigital.get(Integer.parseInt(mrDevice1.getSwitchingPosition()));
                    MrDevice update = new MrDevice();
                    update.setDeviceId(mrDevice1.getDeviceId());
                    update.setLastHeartbeatTime(new Date());
                    update.setWarnStatus(value ? DeviceWarnStatus.ALARM.getValue() : DeviceWarnStatus.LINE.getValue());
                    update.setRemark(String.valueOf(value));
                    mrDeviceService.updateMrDevice(update);
                }
            }
        }
        if (type == 4) {
            List<Integer> samplingChannels = new ArrayList<>();
            for (MrDevice mrDevice1 : mrDevices) {
                samplingChannels.add(Integer.valueOf(mrDevice1.getSwitchingPosition()));
            }
            Map<Integer, Integer> samplingResult = null;
            try {
                samplingResult = tool.readAnalogSampling(samplingChannels);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            String msg = "【canTask】模拟量采样读取结果：" + samplingResult;
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
            if (!samplingResult.isEmpty()) {
                for (MrDevice mrDevice1 : mrDevices) {
                    Integer value = samplingResult.get(Integer.parseInt(mrDevice1.getSwitchingPosition()));
                    MrDevice update = new MrDevice();
                    update.setDeviceId(mrDevice1.getDeviceId());
                    update.setLastHeartbeatTime(new Date());
                    update.setWarnStatus(DeviceWarnStatus.LINE.getValue());
                    update.setRemark(String.valueOf(value));
                    mrDeviceService.updateMrDevice(update);
                }
            }
        }

    }

    @Override
    public void canopenTask() {
        // 获取设备信息
        MrDevice mrDevice = new MrDevice();
        mrDevice.setStatus(DeviceStatus.ACTIVE.getValue());
        List<MrDevice> mrDevices = mrDeviceService.selectMrDeviceList(mrDevice);
        for (MrDevice mr : mrDevices) {
            ModbusConfig config = new ModbusConfig();
            if (prepareModbusConfig(mr, config)) {
                continue;
            }
            CanopenProtocolUtil canopen = new CanopenProtocolUtil(config.getHost(), config.getPort());
            // 读取控制结果
            CanopenProtocolUtil.InputStatusResult result = canopen.readInputStatus(config.getOffset());

            if (result.isSuccess()) {
                String msg = "【canopenTask】32位输出结果：" + new String(result.getTotal32BitChars());
                logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
                MrDevice update = new MrDevice();
                update.setDeviceId(mr.getDeviceId());
                update.setRemark(String.valueOf(result.getTotal32BitChars()[Integer.parseInt(mr.getSwitchingPosition())]));
                boolean flag = DigitStringConstants.ONE.equals(mr.getRemark());
                update.setWarnStatus(flag ? DeviceWarnStatus.ALARM.getValue() : DeviceWarnStatus.LINE.getValue());
                update.setLastHeartbeatTime(new Date());
                mrDeviceService.updateMrDevice(update);
            } else {
                String msg = "【canopenTask】errorMessage：" + result.getErrorMessage();
                logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
            }

        }
    }

    @Override
    public void infoSaveTask() {
        // 获取字典配置
        String waterTankDischargeInfoKey = ConstantsEnum.WATER_TANK_DISCHARGE_INFO.getValue();
        List<SysDictData> sysDictDatas = sysDictDataService.selectDictDataByType(waterTankDischargeInfoKey);
        Map<String, String> dictMap = sysDictDatas.stream()
                .collect(Collectors.toMap(SysDictData::getDictLabel, SysDictData::getDictValue));

        String pumpSelKey = ConstantsEnum.PUMP_SEL.getValue();
        List<SysDictData> sysDictDataPumpSel = sysDictDataService.selectDictDataByType(pumpSelKey);
        Map<String, String> dictPumpSelMap = sysDictDataPumpSel.stream()
                .collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel));

        // 处理两个水泵
        String[][] pumpConfigs = {
                {
                        dictMap.get(ConstantsEnum.BLACK_WATER_PUMP_RUNNING_SIGNAL.getValue()),
                        ConstantsEnum.BLACK_WATER_PUMP_RUNNING_SIGNAL.getValue(),
                        dictMap.get(ConstantsEnum.DOMESTIC_SEWAGE_TANK_HIGH_LEVEL_ALARM.getValue()),
                        dictMap.get(ConstantsEnum.DOMESTIC_SEWAGE_TANK_LIQUID_LEVEL.getValue())
                },
                {
                        dictMap.get(ConstantsEnum.GRAY_WATER_PUMP_RUNNING_SIGNAL.getValue()),
                        ConstantsEnum.GRAY_WATER_PUMP_RUNNING_SIGNAL.getValue(),
                        dictMap.get(ConstantsEnum.GRAY_WATER_TANK_HIGH_LEVEL_ALARM.getValue()),
                        dictMap.get(ConstantsEnum.GRAY_WATER_TANK_LIQUID_LEVEL.getValue())
                }
        };

        for (String[] config : pumpConfigs) {
            String pumpSystemId = config[0];
            String redisKeyPrefix = config[1];
            String alarmSystemId = config[2];
            String levelSystemId = config[3];

            MrDevice pumpDevice = mrDeviceService.getDeviceBySystemId(pumpSystemId);
            MrDevice alarmDevice = mrDeviceService.getDeviceBySystemId(alarmSystemId);
            MrDevice levelDevice = mrDeviceService.getDeviceBySystemId(levelSystemId);

            String redisKey = redisKeyPrefix + pumpSystemId;
            BigDecimal currentLevel = new BigDecimal(levelDevice.getRemark() == null ? "0" : levelDevice.getRemark());

            if (redisCache.hasKey(redisKey)) {
                MrWaterTankDischargeInfo info = redisCache.getCacheObject(redisKey);
                info.setCurrentWaterLevel(currentLevel);
                info.setHasAlarm(alarmDevice.getWarnStatus().equals(DeviceWarnStatus.ALARM.getValue()) ? 1L : 0L);

                if (pumpDevice.getWarnStatus().equals(DeviceWarnStatus.ALARM.getValue())) {
                    info.setDischargeStartTime(new Date());
                    info.setDischargeStartHeight(currentLevel);
                    redisCache.setCacheObject(redisKey, info);
                } else {
                    if (info.getDischargeStartTime() != null) {
                        info.setDischargeEndTime(new Date());
                        info.setDischargeEndHeight(currentLevel);
                        info.setDischargeHeightDiff(info.getDischargeStartHeight().subtract(info.getDischargeEndHeight()));
                    }
                    redisCache.deleteObject(redisKey);
                }
                mrWaterTankDischargeInfoService.updateMrWaterTankDischargeInfo(info);
            } else {
                MrWaterTankDischargeInfo info = new MrWaterTankDischargeInfo();
                info.setUuid(IdUtils.simpleUUID());
                info.setSystemId(pumpSystemId);
                info.setDeviceName(dictPumpSelMap.get(pumpSystemId));
                info.setCurrentWaterLevel(currentLevel);
                info.setHasAlarm(alarmDevice.getWarnStatus().equals(DeviceWarnStatus.ALARM.getValue()) ? 1L : 0L);

                if (pumpDevice.getWarnStatus().equals(DeviceWarnStatus.ALARM.getValue())) {
                    info.setDischargeStartTime(new Date());
                    info.setDischargeStartHeight(currentLevel);
                    redisCache.setCacheObject(redisKey, info);
                }
                mrWaterTankDischargeInfoService.insertMrWaterTankDischargeInfo(info);
            }
        }
    }

    @Override
    public void fuelTransferPumpTask() {
        String waterTankDischargeInfoKey = ConstantsEnum.FUEL_TRANSFER_PUMP.getValue();

        List<SysDictData> sysDictDatas = sysDictDataService.selectDictDataByType(waterTankDischargeInfoKey);
        // 转换为 Map<String, String>，以 dictType 为键，dictValue 为值
        Map<String, String> dictMap = sysDictDatas.stream()
                .collect(Collectors.toMap(
                        SysDictData::getDictLabel,
                        SysDictData::getDictValue
                ));

        String systemId1 = dictMap.get(ConstantsEnum.FUEL_TRANSFER_PUMP_FAULT_ALARM_1.getValue());
        MrDevice mrDevice1 = mrDeviceService.getDeviceBySystemId(systemId1);
        String systemId2 = dictMap.get(ConstantsEnum.FUEL_TRANSFER_PUMP_FAULT_ALARM_2.getValue());
        MrDevice mrDevice2 = mrDeviceService.getDeviceBySystemId(systemId2);
        String systemId3 = dictMap.get(ConstantsEnum.FUEL_TRANSFER_PUMP_AUTOMATIC_CONTROL_OUTPUT_POINT_1.getValue());
        String systemId4 = dictMap.get(ConstantsEnum.FUEL_TRANSFER_PUMP_AUTOMATIC_CONTROL_OUTPUT_POINT_2.getValue());

        String fuelTransferPump = configService.selectConfigByKey(Constants.FUEL_TRANSFER_PUMP);
        if ("1".equals(fuelTransferPump)) {
            if (mrDevice1 == null) {
                return;
            }
            // 1主2备
            if (DeviceWarnStatus.ALARM.getValue().equals(mrDevice1.getWarnStatus())) {
                sensorDataService.associatedAlarm(systemId3, false);
                sensorDataService.associatedAlarm(systemId4, true);
                // 设备输出输出
                mrDeviceService.muteBySystemId(false, systemId3);
                mrDeviceService.muteBySystemId(true, systemId4);
            } else {
                sensorDataService.associatedAlarm(systemId3, true);
                sensorDataService.associatedAlarm(systemId4, false);
                // 设备输出输出
                mrDeviceService.muteBySystemId(true, systemId3);
                mrDeviceService.muteBySystemId(false, systemId4);
            }

        } else {
            if (mrDevice2 == null) {
                return;
            }
            // 2主1备
            if (DeviceWarnStatus.ALARM.getValue().equals(mrDevice2.getWarnStatus())) {
                sensorDataService.associatedAlarm(systemId3, true);
                sensorDataService.associatedAlarm(systemId4, false);
                // 设备输出输出
                mrDeviceService.muteBySystemId(true, systemId3);
                mrDeviceService.muteBySystemId(false, systemId4);
            } else {
                sensorDataService.associatedAlarm(systemId3, false);
                sensorDataService.associatedAlarm(systemId4, true);
                // 设备输出输出
                mrDeviceService.muteBySystemId(false, systemId3);
                mrDeviceService.muteBySystemId(true, systemId4);
            }
        }

    }

    @Override
    public void infoExportTask(String moon) {
        MrWaterTankDischargeInfo mrWaterTankDischargeInfo = new MrWaterTankDischargeInfo();
        mrWaterTankDischargeInfo.setMoon(moon);
        List<MrWaterTankDischargeInfo> list = mrWaterTankDischargeInfoService.selectMrWaterTankDischargeInfoByTimeList(mrWaterTankDischargeInfo);
        ExcelUtil<MrWaterTankDischargeInfo> excelUtil = new ExcelUtil<>(MrWaterTankDischargeInfo.class);

        // 动态指定路径（如 Windows 路径 D:/temp/excel/，Linux 路径 /home/temp/excel/）
        String customPath = "/home/psw/temp/excel/";
        String fullPath = excelUtil.exportExcelToCustomPath(list, "水舱排放信息记录", "水舱排放信息记录列表", customPath);
        System.out.println("Excel导出成功，完整路径：" + fullPath);

        // 休眠2小时（7200000毫秒）
        try {
            // 2小时 * 60分钟 * 60秒 * 1000毫秒
            // 2 * 60 * 60 * 1000
            Thread.sleep(2 * 60 * 60 * 1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.fillInStackTrace();
        }
        mrWaterTankDischargeInfoService.deleteMrWaterTankDischargeInfoByMoon(moon);
    }

    @Override
    public void canopenOutputTask() {
        // 获取设备信息
        MrDevice mrDevice = new MrDevice();
        mrDevice.setStatus(DeviceStatus.ACTIVE.getValue());
        mrDevice.setOutputSource(StringConstants.HYPHEN);
        List<MrDevice> mrDevices = mrDeviceService.selectMrDeviceList(mrDevice);
        Map<String, boolean[]> map = new HashMap<>();
        for (MrDevice mr : mrDevices) {
            ModbusConfig config = new ModbusConfig();
            if (prepareModbusConfig(mr, config)) {
                continue;
            }
            String key = config.getHost() + StringConstants.HYPHEN + config.getPort() + StringConstants.COLON + config.getOffset();
            boolean[] output32Bits;
            if (map.containsKey(key)) {
                output32Bits = map.get(key);
            } else {
                output32Bits = new boolean[32];
            }
            output32Bits[Integer.parseInt(mr.getSwitchingPosition())] = DigitStringConstants.ONE.equals(mr.getRemark());
            map.put(key, output32Bits);
        }
        for (Map.Entry<String, boolean[]> entry : map.entrySet()) {
            String key = entry.getKey();
            String host = key.substring(0, key.indexOf(StringConstants.HYPHEN));
            String port = key.split(StringConstants.COLON)[0].split(StringConstants.HYPHEN)[1];
            String offset = key.split(StringConstants.COLON)[1];
            boolean[] output32Bits = map.get(key);
            CanopenProtocolUtil canopen = new CanopenProtocolUtil(host, Integer.parseInt(port));
            // 发送32位输出控制命令
            CanopenResponse response = canopen.controlOutput(Integer.parseInt(offset), output32Bits);
            String msg = "【canopenOutputTask】入参：" + Arrays.toString(output32Bits);
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
            if (response.isSuccess()) {
                // 更新通道状态
                sensorDataService.updateDeviceStatus(port);
            } else {
                // 通道报警
                sensorDataService.channelAlarm(Integer.parseInt(port));
            }
        }

    }

    @Override
    public void outputDeviceData() {
        // 获取设备信息
        MrDevice mrDevice = new MrDevice();
        mrDevice.setStatus(DeviceStatus.ACTIVE.getValue());
//        mrDevice.setOutputSource(StringConstants.HYPHEN);
        List<MrDevice> mrDevices = mrDeviceService.selectMrDeviceList(mrDevice);
        for (MrDevice mr : mrDevices) {
            String value = resolveOutputValue(mr);
            if (value == null) {
                continue;
            }
            ModbusConfig config = new ModbusConfig();
            if (prepareModbusConfig(mr, config)) {
                continue;
            }
            boolean flag;
            try {
                // 模拟量
                if (mr.getModality().equals(ProductEnum.BA.getValue())) {
                    if (StringUtils.isEmpty(value) || StringUtils.isEmpty(mr.getOutputRange())) {
                        continue;
                    }
                    // 指令3
                    short writeValue = (short) ((Double.parseDouble(value) * NumberConstants.SIXTEEN / Double.parseDouble(mr.getOutputRange()) + NumberConstants.FOUR) * NumberConstants.ONE_THOUSAND);
                    flag = Modbus4jWriteUtils.writeRegister(config.getHost(), config.getPort(), config.getSlaveId(), config.getOffset(), writeValue);
                    String msg = "【模拟量输出成功】host:" + config.getHost() + "port:" + config.getPort() + "offset:" + config.getOffset() + "writeValue:" + writeValue + "flag:" + flag;
                    logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
                }
                // 开关量
                if (mr.getModality().equals(ProductEnum.MR.getValue())) {
                    // 指令1
                    boolean writeValue = Boolean.parseBoolean(value);
                    flag = Modbus4jWriteUtils.writeCoil(config.getHost(), config.getPort(), config.getSlaveId(), config.getOffset(), writeValue);
                    String msg = "【开关量输出成功】host:" + config.getHost() + "port:" + config.getPort() + "offset:" + config.getOffset() + "writeValue:" + writeValue + "flag:" + flag;
                    logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
                }
                mr.setRemark(value);
                // 更新设备状态
                MrDevice mrDevice1 = new MrDevice();
                mrDevice1.setDeviceId(mr.getDeviceId());
                mrDevice1.setRemark(mr.getRemark());
                // TODO 一主二备 要屏蔽这个状态
                mrDevice1.setWarnStatus(DeviceWarnStatus.LINE.getValue());
                mrDevice1.setLastHeartbeatTime(new Date());
                mrDeviceService.updateMrDevice(mrDevice1);
                // 更新通道状态
                sensorDataService.updateDeviceStatus(mr.getPort());
            } catch (ModbusTransportException | ModbusInitException e) {
                // 通道报警
                sensorDataService.channelAlarm(config.getPort());
            }
        }
    }

    private String resolveOutputValue(MrDevice device) {
        if (StringUtils.isEmpty(device.getOutputSource())) {
            log.debug("设备ID={}的输出源为空", device.getDeviceId());
            return null;
        }
        if (!device.getOutputSource().contains(StringConstants.HYPHEN)) {
            log.debug("设备ID={}的输出源格式不支持", device.getDeviceId());
            return null;
        }

        MrDevice sourceDevice = mrDeviceService.getDeviceBySystemId(device.getOutputSource());
        if (sourceDevice == null) {
            log.warn("未找到输出源设备: {}", device.getOutputSource());
            return null;
        }

        if (!DeviceStatus.ACTIVE.getValue().equals(sourceDevice.getStatus())) {
            log.warn("输出源设备状态不活跃: {}", sourceDevice.getDeviceId());
            return null;
        }

        if (DeviceWarnStatus.NORMAL.getValue().equals(sourceDevice.getWarnStatus())) {
            log.debug("输出源设备告警状态正常: {}", sourceDevice.getDeviceId());
            return null;
        }

        if (StringUtils.isEmpty(sourceDevice.getRemark())) {
            log.warn("输出源设备备注为空: {}", sourceDevice.getDeviceId());
            return null;
        }
        device.setWarnStatus(sourceDevice.getWarnStatus());
        return sourceDevice.getRemark();
    }

    private boolean prepareModbusConfig(MrDevice device, ModbusConfig config) {

        if (StringUtils.isEmpty(device.getHost())) {
            log.warn("设备ID={}主机地址为空", device.getDeviceId());
            return true;
        }
        config.setHost(device.getHost());

        try {
            config.setPort(Integer.parseInt(device.getPort()));
        } catch (NumberFormatException e) {
            log.warn("设备ID={}端口号无效: {}", device.getDeviceId(), device.getPort());
            return true;
        }

        try {
            config.setOffset(Integer.parseInt(device.getRegisterAddress()));
        } catch (NumberFormatException e) {
            log.warn("设备ID={}寄存器地址无效: {}", device.getDeviceId(), device.getRegisterAddress());
            return true;
        }
        // 默认从站ID
        config.setSlaveId(NumberConstants.ONE);

        return false;
    }

    private void sendDataBatch(DatagramSocket socket, InetAddress address, int port, List<Map<String, String>> dataList, String cate) {
        try {
            Map<String, Object> payload = new HashMap<>();
            payload.put("devId", "jjtf");
            payload.put("cate", cate);
            payload.put("data", dataList);
            ObjectMapper objectMapper = new ObjectMapper();
            String jsonData = objectMapper.writeValueAsString(payload);
            byte[] sendData = jsonData.getBytes();

            DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, port);
            socket.send(sendPacket);
            String msg = "已发送 UDP 数据到 " + address.getHostAddress() + ":" + port + "，数据内容：" + jsonData;
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
        } catch (JsonProcessingException e) {
            log.error("JSON 处理错误: {}", e.getMessage());
        } catch (IOException e) {
            log.error("UDP 发送错误: " + e.getMessage());
        }
    }

    private void sendDataBatch(DatagramSocket socket, InetAddress address, int port, String dataList, String cate) {
        try {
            byte[] sendData = dataList.getBytes();

            DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, port);
            socket.send(sendPacket);
            String msg = "已发送 UDP 数据到 " + address.getHostAddress() + ":" + port + "，数据内容：" + dataList;
            logUtil.info(ClassMethodNameUtil.getSimpleMethodName(), msg);
        } catch (IOException e) {
            log.error("UDP 发送错误: " + e.getMessage());
        }
    }

}
